// http://en.wikipedia.org/wiki/Bzip2#Compression_stack
// TODO: pocitat s nejhorsim moznym pripadem!! pri expanzi pretece buffer!!
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

#include "../config.h"

#include "../RLE/RLE.h"
#include "../BWT/BWT.h"
#include "../ModeToFront/MTF.h"
#include "../Huffman/Huffman.h"
#include "../Adler-32 checksum/Adler32.h"

int main()
{
	string file_dec;
	string file_enc;
	cout << "File to decompress: ";
	cin >> file_enc;
	cout << "Decompressed file: ";
	cin >> file_dec;
	//
	unsigned char *buffer = new unsigned char[Config::BLOCK_SIZE];
	ifstream fin(file_enc, ios_base::in | ios_base::binary);
	if(!fin.is_open()) { cerr << "error" << endl; return 0; }
	ofstream fout(file_dec, ios_base::out | ios_base::binary);
	if(!fout.is_open()) { cerr << "error" << endl; return 0; }
	//
	streamsize read;
	unsigned int bwt_dec_start, enc_len, dec_len, checksum;
	unsigned char *encoded = new unsigned char[Config::BLOCK_SIZE];
	unsigned char *decoded = new unsigned char[Config::BLOCK_SIZE];
	unsigned short *rle_ab_enc = new unsigned short[Config::BLOCK_SIZE];
	// check file type
	fin.read((char *)buffer, 3);
	if(buffer[0] != 'P' || buffer[1] != 'R' || buffer[2] != 'C')
    {
        cerr << "Invalid format!" << endl;
        return 1;
    }
	// decompression
    while(1)
	{
		fin.read((char *)&enc_len, sizeof(unsigned int));		// read a length of a compressed block
		fin.read((char *)buffer, enc_len+sizeof(unsigned int));	// read a compressed block of length `enc_len` and read the checksum for this block
		if((read = fin.gcount()) > 0)	// get the count of read bytes in the last read operation
		{
			enc_len = read - 4;
            if(!Adler32::Check(buffer, enc_len, *((unsigned int *)(buffer+enc_len))))	// read and check the checksum
            {
                cerr << "File is corrupted. Invalid checksum!" << endl;
                return 2;
            }
			// decompress one block
            Huffman::Decode(enc_len, buffer, rle_ab_enc, enc_len);
            RLE::Decode_AB(rle_ab_enc, enc_len, encoded, enc_len);
            bwt_dec_start = ((int *)encoded)[0]; enc_len -= 4;
            MTF::Decode(encoded+4, enc_len, decoded);
            BWT::Decode(enc_len, decoded, bwt_dec_start, encoded);
            RLE::Decode_MN5(encoded, enc_len, decoded, dec_len);
            // write out the decompressed data
            fout.write((char *)decoded, dec_len);
		}
		if(fin.eof()) break;
	}
	fout.close();
	fin.close();
	//
	delete [] buffer;
	delete [] encoded;
	delete [] decoded;
	delete [] rle_ab_enc;
	//
	return 0;
}